using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Util;

namespace ICSharpCode.TextEditor
{
    [ToolboxItem(false)]
    public class TextAreaControl : Panel
    {
        private Padding padding = new Padding(2, 2, 2, 2);

        private TextEditorControl motherTextEditorControl;

        private HRuler hRuler = null;

        private TextEditorScrollBar vScrollBar = new TextEditorVScrollBar();

        private TextEditorScrollBar hScrollBar = new TextEditorHScrollBar();

        private TextArea textArea;

        private bool doHandleMousewheel = true;

        private bool disposed;

        private bool hasTextChanged = false;

        private int scrollMarginHeight = 3;

        private Timer TextChangedTimer = null;

        private bool adjustScrollBarsOnNextUpdate;

        private Point scrollToPosOnNextUpdate;

        public ScrollEventHandler ScrollBarValueChanged;

        private MouseWheelHandler mouseWheelHandler = new MouseWheelHandler();

        public bool HasTextChanged => hasTextChanged;

        public TextArea TextArea => textArea;

        public SelectionManager SelectionManager => textArea.SelectionManager;

        public Caret Caret => textArea.Caret;

        [Browsable(false)]
        public IDocument Document
        {
            get
            {
                if (motherTextEditorControl != null)
                {
                    return motherTextEditorControl.Document;
                }
                return null;
            }
        }

        public ITextEditorProperties TextEditorProperties
        {
            get
            {
                if (motherTextEditorControl != null)
                {
                    return motherTextEditorControl.TextEditorProperties;
                }
                return null;
            }
        }

        public TextEditorScrollBar VScrollBar => vScrollBar;

        public TextEditorScrollBar HScrollBar => hScrollBar;

        public bool DoHandleMousewheel
        {
            get
            {
                return doHandleMousewheel;
            }
            set
            {
                doHandleMousewheel = value;
            }
        }

        public event MouseEventHandler ShowContextMenu;

        public TextAreaControl(TextEditorControl motherTextEditorControl)
        {
            TextChangedTimer = new Timer();
            TextChangedTimer.Enabled = false;
            TextChangedTimer.Interval = 150;
            this.motherTextEditorControl = motherTextEditorControl;
            textArea = new TextArea(motherTextEditorControl, this);
            base.Controls.Add(textArea);
            vScrollBar.ValueChanged += VScrollBarValueChanged;
            base.Controls.Add(vScrollBar);
            hScrollBar.ValueChanged += HScrollBarValueChanged;
            base.Controls.Add(hScrollBar);
            base.ResizeRedraw = true;
            Document.TextContentChanged += DocumentTextContentChanged;
            Document.DocumentChanged += AdjustScrollBarsOnDocumentChange;
            Document.UpdateCommited += DocumentUpdateCommitted;
            TextChangedTimer.Tick += TextChangedTimer_Tick;
        }

        private void TextChangedTimer_Tick(object sender, EventArgs e)
        {
            if (textArea != null && textArea.Document != null && textArea.Document.TextEditorProperties.Font != null && textArea.Document.TextEditorProperties.ShowGuidelines && hasTextChanged)
            {
                hasTextChanged = false;
                textArea.Document.GuidelinesManager.Update(textArea);
            }
        }

        protected override void Dispose(bool disposing)
        {
            TextChangedTimer.Enabled = false;
            TextChangedTimer.Dispose();
            if (disposing && !disposed)
            {
                disposed = true;
                Document.TextContentChanged -= DocumentTextContentChanged;
                Document.DocumentChanged -= AdjustScrollBarsOnDocumentChange;
                Document.UpdateCommited -= DocumentUpdateCommitted;
                motherTextEditorControl = null;
                if (vScrollBar != null)
                {
                    vScrollBar.Dispose();
                    vScrollBar = null;
                }
                if (hScrollBar != null)
                {
                    hScrollBar.Dispose();
                    hScrollBar = null;
                }
                if (hRuler != null)
                {
                    hRuler.Dispose();
                    hRuler = null;
                }
            }
            base.Dispose(disposing);
        }

        private bool VirtualTopEquals(Point sourcePoint, Point targetPoint)
        {
            if (targetPoint.X != sourcePoint.X || targetPoint.Y != sourcePoint.Y)
            {
                return false;
            }
            return true;
        }

        private void DocumentTextContentChanged(object sender, EventArgs e)
        {
            Caret.ValidateCaretPos();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            ResizeTextArea();
        }

        public void ResizeTextArea()
        {
            SetScrollBarBounds();
        }

        public void SetScrollBarBounds()
        {
            int num = 0;
            Rectangle clientRectangle = base.ClientRectangle;
            checked
            {
                int num2 = Document.GetVisibleLine(Document.TotalNumberOfLines + 1) * textArea.TextView.FontHeight;
                if (num2 > clientRectangle.Height)
                {
                    vScrollBar.Visible = true;
                }
                else
                {
                    vScrollBar.Visible = false;
                }
                int visibleColumnCount = textArea.GetVisibleColumnCount();
                if (visibleColumnCount > textArea.TextView.VisibleColumnCount)
                {
                    hScrollBar.Visible = true;
                }
                else
                {
                    hScrollBar.Visible = false;
                }
                int num3 = 0;
                int num4 = 0;
                if (hRuler != null && hRuler.Visible)
                {
                    hRuler.Bounds = new Rectangle(0, 0, base.Width - (vScrollBar.Visible ? 18 : 0), 26);
                    num3 = hRuler.Bounds.Bottom;
                    num4 = hRuler.Bounds.Height;
                }
                textArea.Bounds = new Rectangle(padding.Left, num3 + padding.Top, base.Width - padding.Left - padding.Right - (vScrollBar.Visible ? 18 : 0), base.Height - padding.Top - padding.Bottom - (hScrollBar.Visible ? 18 : 0) - num4);
                vScrollBar.Bounds = new Rectangle(textArea.Bounds.Right + padding.Right, 0, 18, base.Height - (hScrollBar.Visible ? (18 + num - 1) : 0));
                hScrollBar.Bounds = new Rectangle(0, textArea.Bounds.Bottom + padding.Bottom, base.Width - (vScrollBar.Visible ? (18 + num - 1) : 0), 18);
            }
        }

        private void AdjustScrollBarsOnDocumentChange(object sender, DocumentEventArgs e)
        {
            if (!motherTextEditorControl.IsInUpdate)
            {
                AdjustScrollBarsClearCache();
                AdjustScrollBars();
            }
            else
            {
                adjustScrollBarsOnNextUpdate = true;
            }
        }

        private void DocumentUpdateCommitted(object sender, EventArgs e)
        {
            if (!motherTextEditorControl.IsInUpdate)
            {
                Caret.ValidateCaretPos();
                if (!scrollToPosOnNextUpdate.IsEmpty)
                {
                    ScrollTo(scrollToPosOnNextUpdate.Y, scrollToPosOnNextUpdate.X);
                }
                if (adjustScrollBarsOnNextUpdate)
                {
                    AdjustScrollBarsClearCache();
                    AdjustScrollBars();
                }
            }
        }

        private void AdjustScrollBarsClearCache()
        {
        }

        public void AdjustScrollBars()
        {
            adjustScrollBarsOnNextUpdate = false;
            vScrollBar.Minimum = 0;
            vScrollBar.Maximum = textArea.MaxVScrollValue;
            vScrollBar.LargeChange = Math.Max(0, textArea.TextView.DrawingPosition.Height);
            vScrollBar.SmallChange = Math.Max(0, textArea.TextView.FontHeight);
            hScrollBar.Minimum = 0;
            hScrollBar.Maximum = textArea.MaxHScrollValue;
            hScrollBar.LargeChange = Math.Max(0, checked(textArea.TextView.VisibleColumnCount - 1));
            hScrollBar.SmallChange = Math.Max(0, textArea.TextView.SpaceWidth);
            SetScrollBarBounds();
        }

        public void OptionsChanged()
        {
            textArea.OptionsChanged();
            if (textArea.TextEditorProperties.ShowHorizontalRuler)
            {
                if (hRuler == null)
                {
                    hRuler = new HRuler(textArea);
                    base.Controls.Add(hRuler);
                    ResizeTextArea();
                }
                else
                {
                    hRuler.Invalidate();
                }
            }
            else if (hRuler != null)
            {
                base.Controls.Remove(hRuler);
                hRuler.Dispose();
                hRuler = null;
                ResizeTextArea();
            }
            AdjustScrollBars();
        }

        public void UpdateVirtualTop()
        {
            Point value = new Point(checked(hScrollBar.Value * textArea.TextView.WideSpaceWidth), vScrollBar.Value);
            textArea.UpdateVirtualTop(value);
        }

        private void VScrollBarValueChanged(object sender, EventArgs e)
        {
            if (!TextEditorProperties.ComparisonState)
            {
                textArea.VirtualTop = new Point(textArea.VirtualTop.X, vScrollBar.Value);
            }
            if (ScrollBarValueChanged != null)
            {
                ScrollBarValueChanged(sender, new ScrollEventArgs(ScrollEventType.ThumbPosition, vScrollBar.Value));
            }
        }

        private void HScrollBarValueChanged(object sender, EventArgs e)
        {
            if (!TextEditorProperties.ComparisonState)
            {
                textArea.VirtualTop = new Point(checked(hScrollBar.Value * textArea.TextView.WideSpaceWidth), textArea.VirtualTop.Y);
            }
            if (ScrollBarValueChanged != null)
            {
                ScrollBarValueChanged(sender, new ScrollEventArgs(ScrollEventType.ThumbPosition, vScrollBar.Value));
            }
        }

        public void HandleMouseWheel(MouseEventArgs e)
        {
            int wheelDeltaScrollDistance = mouseWheelHandler.GetWheelDeltaScrollDistance(e);
            if (wheelDeltaScrollDistance != 0)
            {
                if ((Control.ModifierKeys & Keys.Control) != 0 && TextEditorProperties.MouseWheelTextZoom)
                {
                    Font font = textArea.Document.TextEditorProperties.Font;
                    font = ((wheelDeltaScrollDistance <= 0) ? new Font(font.Name, Math.Max(6f, font.Size - 1f)) : new Font(font.Name, font.Size + 1f));
                    textArea.Document.TextEditorProperties.Font = font;
                    OptionsChanged();
                }
                else
                {
                    int num = Math.Abs(e.Delta) / 120;
                    int val = checked((SystemInformation.MouseWheelScrollLines <= 0) ? (vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : (-1)) * Math.Sign(e.Delta) * vScrollBar.LargeChange) : (vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : (-1)) * Math.Sign(e.Delta) * SystemInformation.MouseWheelScrollLines * vScrollBar.SmallChange * num));
                    vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum, val));
                }
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (DoHandleMousewheel)
            {
                HandleMouseWheel(e);
            }
        }

        public void DoTextAreaTextChanged(EventArgs e)
        {
            Document.GuidelinesManager.Clear(textArea);
            hasTextChanged = true;
        }

        public void ScrollToCaret()
        {
            ScrollTo(textArea.Caret.Line, textArea.Caret.Column);
        }

        public void ScrollTo(int line, int column)
        {
            if (motherTextEditorControl.IsInUpdate)
            {
                scrollToPosOnNextUpdate = new Point(column, line);
                return;
            }
            scrollToPosOnNextUpdate = Point.Empty;
            ScrollTo(line);
            checked
            {
                int num = hScrollBar.Value - hScrollBar.Minimum;
                int num2 = num + textArea.TextView.VisibleColumnCount;
                int visualColumn = textArea.TextView.GetVisualColumn(line, column);
                if (textArea.TextView.VisibleColumnCount < 0)
                {
                    hScrollBar.Value = 0;
                }
                else if (visualColumn < num)
                {
                    hScrollBar.Value = Math.Max(0, visualColumn - scrollMarginHeight);
                }
                else if (visualColumn > num2)
                {
                    hScrollBar.Value = Math.Max(0, Math.Min(hScrollBar.Maximum, visualColumn - textArea.TextView.VisibleColumnCount + scrollMarginHeight));
                }
            }
        }

        public void ScrollTo(int line)
        {
            checked
            {
                line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
                line = Document.GetVisibleLine(line);
                int num = textArea.TextView.FirstPhysicalLine;
                if (textArea.TextView.LineHeightRemainder > 0)
                {
                    num++;
                }
                if (line - scrollMarginHeight + 3 < num)
                {
                    vScrollBar.Value = Math.Max(0, Math.Min(vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight));
                    VScrollBarValueChanged(this, EventArgs.Empty);
                    return;
                }
                int num2 = num + textArea.TextView.VisibleLineCount;
                if (line + scrollMarginHeight - 1 > num2)
                {
                    if (textArea.TextView.VisibleLineCount == 1)
                    {
                        vScrollBar.Value = Math.Max(0, Math.Min(vScrollBar.Maximum, (line - scrollMarginHeight - 1) * textArea.TextView.FontHeight));
                    }
                    else
                    {
                        vScrollBar.Value = Math.Min(vScrollBar.Maximum, (line - textArea.TextView.VisibleLineCount + scrollMarginHeight - 1) * textArea.TextView.FontHeight);
                    }
                    VScrollBarValueChanged(this, EventArgs.Empty);
                }
            }
        }

        public void CenterViewOn(int line, int treshold)
        {
            checked
            {
                line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
                line = Document.GetVisibleLine(line);
                line -= unchecked(textArea.TextView.VisibleLineCount / 2);
                int num = textArea.TextView.FirstPhysicalLine;
                if (textArea.TextView.LineHeightRemainder > 0)
                {
                    num++;
                }
                if (Math.Abs(num - line) > treshold)
                {
                    vScrollBar.Value = Math.Max(0, Math.Min(vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight));
                    VScrollBarValueChanged(this, EventArgs.Empty);
                }
            }
        }

        public void JumpTo(int line)
        {
            checked
            {
                line = Math.Max(0, Math.Min(line, Document.TotalNumberOfLines - 1));
                string text = Document.GetText(Document.GetLineSegment(line));
                JumpTo(line, text.Length - text.TrimStart().Length);
            }
        }

        public void JumpTo(int line, int column)
        {
            textArea.Focus();
            textArea.SelectionManager.ClearSelection();
            textArea.Caret.Position = new TextLocation(column, line);
            textArea.SetDesiredColumn();
            ScrollToCaret();
        }

        public void Highlight(int offset, int length)
        {
            if (offset >= 0 && length >= 1)
            {
                int offset2 = checked(offset + length);
                TextLocation startPosition = Document.OffsetToPosition(offset);
                TextLocation endPosition = Document.OffsetToPosition(offset2);
                SelectionManager.SetSelection(startPosition, endPosition);
            }
        }

        public void AddLineCustomColor(CustomLineColor color, int lineNumber)
        {
            Document.AddLineCustomColor(color, lineNumber);
        }

        public void LineCustomColorsClear()
        {
            Document.LineCustomColorsClear();
        }

        internal void SetTextChangedTimerEnable(bool enable)
        {
            if (TextChangedTimer.Enabled != enable)
            {
                TextChangedTimer.Enabled = enable;
            }
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 123 && ShowContextMenu != null)
            {
                long num = m.LParam.ToInt64();
                int num2 = (short)(num & 0xFFFF);
                int num3 = (short)((num & 0xFFFF0000u) >> 16);
                if (num2 == -1 && num3 == -1)
                {
                    Point screenPosition = Caret.ScreenPosition;
                    ShowContextMenu(this, new MouseEventArgs(MouseButtons.None, 0, screenPosition.X, checked(screenPosition.Y + textArea.TextView.FontHeight), 0));
                }
                else
                {
                    Point point = PointToClient(new Point(num2, num3));
                    ShowContextMenu(this, new MouseEventArgs(MouseButtons.Right, 1, point.X, point.Y, 0));
                }
            }
            base.WndProc(ref m);
        }

        protected override void OnEnter(EventArgs e)
        {
            try
            {
                if (motherTextEditorControl != null)
                    motherTextEditorControl.SetActiveTextAreaControl(this);

                Caret.ValidateCaretPos();
                base.OnEnter(e);
            }
            catch
            {

            }

        }
    }
}
